package com.xq.jltx.kotlinandroid

import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import android.content.SharedPreferences
import android.os.SystemClock
import android.text.TextUtils
import android.util.Log
import com.google.gson.Gson
import com.xq.jltx.kotlinandroid.mmkv.getKey
import java.io.*
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import java.lang.reflect.Type
import kotlin.reflect.KProperty

/**
 * @author aidenquan
 *         Created by junlintianxia on 2021/06/30.
 */
abstract class PreferencesDelegate<T> {
    private var key: String = ""
    private var defValue: T
    private var type: Type?

    @JvmOverloads
    constructor(
        key: String,
        defValue: T,
        type: Type? = null
    ) {
        this.key = key
        this.defValue = defValue
        this.type = type
    }

    protected abstract val application: Application

    /**a
     * SharedPreferences file name. `default_preferences.xml`.
     */
    protected open val name: String = "default_preferences"

    private val sp by lazy {
        application.getSharedPreferences(name, Context.MODE_PRIVATE)
    }

    private val gson by lazy {
        Gson()
    }


    @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return getSPCore(getKey(key, property), defValue, type)
    }


    @SuppressLint("CommitPrefEdits")
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
        setSPCore(getKey(key, property), value)
    }

    @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
    @JvmOverloads
    fun <T> getSPCore(key: String, defValue: T, type: Type? = null): T {
        val lastTime = SystemClock.elapsedRealtime()
        val value =  with(sp) {
            when (defValue) {
                is String -> getString(key, defValue)
                is Int -> getInt(key, defValue)
                is Long -> getLong(key, defValue)
                is Float -> getFloat(key, defValue)
                is Boolean -> getBoolean(key, defValue)
                else -> {
                    val str = getString(key, "");
                    if (TextUtils.isEmpty(str) || type == null) {
                        defValue
                    } else {
                        gson.fromJson(str, type)
                    }
                }
            }
        } as T
        Log.d(
            "HHHHHH",
            "getSPCore name:${name}, key:${key}, timer:${SystemClock.elapsedRealtime() - lastTime}, value:${value}"
        )
        return value
    }

    @JvmOverloads
    fun setSPCore(key: String, value: Any?, isSync: Boolean = false) {
        val lastTime = SystemClock.elapsedRealtime()
        try {
            SharedPreferencesCompat.apply(with(sp.edit()) {
                when (value) {
                    null -> remove(key)
                    is String -> putString(key, value)
                    is Int -> putInt(key, value)
                    is Long -> putLong(key, value)
                    is Float -> putFloat(key, value)
                    is Boolean -> putBoolean(key, value)
                    else -> {
                        putString(key, gson.toJson(value))
                    }
                }

            }, isSync)
        } catch (e: Exception) {
        }
        Log.d(
            "HHHHHH",
            "setSPCore name:${name}, key:${key}, timer:${SystemClock.elapsedRealtime() - lastTime}, value:${value}"
        )
    }

    fun clearSPCore() {
        SharedPreferencesCompat.apply(sp.edit().clear())
    }

    fun clearSPCore(key: String) {
        SharedPreferencesCompat.apply(sp.edit().remove(key))
    }

    fun containsCore(key: String): Boolean {
        return sp.contains(key)
    }

    fun getAllCore(): Map<String, *> {
        return sp.all
    }

    /**
     * 创建一个解决SharedPreferencesCompat.apply方法的一个兼容类
     *
     * @author zhy
     * update by junlintianxia
     */
    private object SharedPreferencesCompat {
        private val sApplyMethod = findApplyMethod()

        /**
         * 反射查找apply的方法
         *
         * @return
         */
        private fun findApplyMethod(): Method? {
            try {
                val clz: Class<*> = SharedPreferences.Editor::class.java
                return clz.getMethod("apply")
            } catch (e: NoSuchMethodException) {
            }
            return null
        }
        /**
         * 如果找到则使用apply执行，否则使用commit，如果isSync为true，直接调用commit
         *
         * @param editor
         * @param isSync
         */
        /**
         * 如果找到则使用apply执行，否则使用commit
         *
         * @param editor
         */
        @JvmOverloads
        fun apply(editor: SharedPreferences.Editor, isSync: Boolean = false) {

            if (!isSync) {
                try {
                    if (sApplyMethod != null) {
                        sApplyMethod.invoke(editor)
                        return
                    }
                } catch (e: IllegalArgumentException) {
                } catch (e: IllegalAccessException) {
                } catch (e: InvocationTargetException) {
                }
            }
            editor.commit()
        }
    }
}